﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Utilies;
using System.Timers;
using System.Windows.Forms;


namespace EntidadesDeNegocio
{
    public class Sistema: Observable
    {
        /// <summary>
        /// Instancia privada del singleton
        /// </summary>
        private static Sistema _Instancia;
        /// <summary>
        /// Obejeto para realizar lockeo de hilo
        /// </summary>
        private static object Lockea = new object();
        #region Singleton
        /// <summary>
        /// Instancia de clase (singleton)
        /// </summary>
        public static Sistema Instancia 
        {
            get 
            {
                if (_Instancia == null)
                {
                    lock (Lockea) 
                    {
                        if (_Instancia == null)
                            _Instancia = new Sistema();
                    }
                }
                return _Instancia;
            }
        }
        #endregion
        /// <summary>
        /// Lista de diseños del sistema
        /// </summary>
        public List<Diseno> ListaDisenos { get; set; }
        /// <summary>
        /// Intervalo en segundos para una producción
        /// </summary>
        public int SegundosProduccion {get;set;}
        /// <summary>
        /// Cantidad de robot que se producen al pasar el intervalo de produccion
        /// </summary>
        public int CantidadRobotAProducir { get; set; }
        /// <summary>
        /// IdProduccion autogenerado
        /// </summary>
        private int IdProduccionAuto{ get; set; }
        /// <summary>
        /// IdRobotAuto autogenerado
        /// </summary>
        private int IdRobotAuto { get; set; }
        /// <summary>
        /// Timer de producción
        /// </summary>
        public System.Timers.Timer TimerProduccion { get; set; }
        /// <summary>
        /// LIsta de producciones del sistema
        /// </summary>
        public List<Produccion> ListaProducciones { get; set; }       
        /// <summary>
        /// Constructor sin parametros de forma privada para no permitir que se instancie el objeto.
        /// </summary>
        private Sistema() 
        {
            ListaDisenos = new List<Diseno>();
            ListaProducciones = new List<Produccion>();
            ListaObservadores = new List<IObservador>();
            IdProduccionAuto = 0;
            IdRobotAuto = 0;
            SegundosProduccion = 0;
            CantidadRobotAProducir = 0;
        }
        /// <summary>
        /// Agrega un diseño
        /// </summary>
        /// <param name="diseno"></param>
        public bool AgregarDiseno(Diseno diseno)
        {
            if (!ExisteDiseno(diseno.Nombre))
            {
                if (diseno.ListaComponentes.Count() != 0)
                {
                    ListaDisenos.Add(diseno);
                    ActualizarTodo(this);
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Busca un diseño por nombre y retorna verdadero si lo encientra
        /// </summary>
        /// <param name="nombreDiseno"></param>
        /// <returns></returns>
        public bool ExisteDiseno(String nombreDiseno)
        {
            return ObtenerDiseno(nombreDiseno) != null;
        }

        /// <summary>
        /// Agrega un diseño al sistema y recibe sus popiedades por parametro
        /// </summary>
        /// <param name="nombre"></param>
        /// <param name="medioComponente"></param>
        public bool AgregarDiseno(String nombre, MedioComponente medioComponente,decimal costoAgregado, List<Componente> lstComponentes)
        {

            Diseno diseno = new Diseno(nombre,medioComponente,costoAgregado);
            diseno.ListaComponentes = lstComponentes;
            if (!ListaDisenos.Contains(diseno))
            {
                return this.AgregarDiseno(diseno);
            }
            return false;
        }
        /// <summary>
        /// Elimina un diseño
        /// </summary>
        /// <param name="diseno"></param>
        public void EliminarDiseno(Diseno diseno) 
        {
            ListaDisenos.Remove(diseno);
        }

        /// <summary>
        /// Elimina un Diseno del Sistema en funcion de su nombre
        /// </summary>
        /// <param name="nombreDiseno"></param>
        public void EliminarDiseno(String nombreDiseno) 
        {
            Diseno disenoABuscar = new Diseno();
            disenoABuscar.Nombre = nombreDiseno;           
            EliminarDiseno(disenoABuscar);
        
        }

         /// <summary>
        /// Obtengo un Diseno por su nombre
        /// </summary>
        /// <param name="nombre"></param>
        /// <returns></returns>
        public Diseno ObtenerDiseno(string nombre)
        {

            Diseno dis = null;
            if (ListaDisenos.Count() != 0)
            {
                try
                {
                    dis = (from Diseno d in ListaDisenos where d.Nombre.ToLower() == nombre.ToLower() select d).First();
                }
                catch
                {

                }
            }
            return dis;
        }

        /// <summary>
        /// Modifica el costo agregado y la lista de componentes de un diseno segun su nombre
        /// </summary>
        /// <param name="nombre"></param>
        /// <param name="componentes"></param>
        /// <returns></returns>
        public bool ModificarDiseno(String nombre,decimal costoAgregado,List<Componente> componentes)
        {
            Diseno diseno = ObtenerDiseno(nombre);
            diseno.CostoAgregado = costoAgregado;
            diseno.ListaComponentes = componentes;
            ActualizarTodo(this);
            return true;
        }
        /// <summary>
        /// Agrega una produccion
        /// </summary>
        /// <param name="produccion"></param>
        public void AgregarProduccion(Diseno diseno,MedioComponente medio,int cantidadSolicitada,int cantidadProducida,bool producir) 
        {
            IdProduccionAuto++;
            Produccion produccion = new Produccion(IdProduccionAuto,diseno, medio, cantidadSolicitada, 0, DateTime.Now,producir);
            ListaProducciones.Add(produccion);
        }
        /// <summary>
        /// Elimina una produccion
        /// </summary>
        /// <param name="produccion"></param>
        public void EliminarProduccion(int idProduccion)
        {
            Produccion prod = ObtenerProduccion(idProduccion);
            ListaProducciones.Remove(prod);
        }
        /// <summary>
        /// Obtiene la produccion con determinado idProducción
        /// </summary>
        /// <param name="idProduccion"></param>
        /// <returns></returns>
        public Produccion ObtenerProduccion(int idProduccion) 
        {
            Produccion prod;
            prod=(from Produccion p in ListaProducciones where p.IdProduccion == idProduccion select p).First();
            return prod;
        }
        /// <summary>
        /// Produce un lista de robot
        /// </summary>
        /// <param name="producciones"></param>
        public void Producir() 
        {
            int ContadorRobots=0;
            int ContadorCantidadSolicitada = 0;
            Componente componenteReal=null;
            
            foreach (Produccion prod in ListaComponentesOrdenada()) 
            {
                
                //Produce solo las producciones que estan seteadas para producir
                //No sobrepase la cantidad de robots a producir seteadas en el sistema
                //Cantidad restante a producir diferente de 0
                if (prod.Producir && this.CantidadRobotAProducir > ContadorRobots && prod.CantidadRestante != 0)
                {
                    //Verifica que la produccion se pueda realizar
                    if (!VerificoProduccion(prod))
                        throw new Exception();
                    //Creo el componente en el stock si es que no existe
                    if (Stock.Instancia.ObtenerComponente(prod.DisenoProduccion.Nombre) == null)
                    {
                        Stock.Instancia.AgregarComponente(prod.DisenoProduccion.Nombre, "ROB-" + prod.DisenoProduccion.Nombre + IdRobotAuto.ToString("00000"), prod.DisenoProduccion.Medio, prod.DisenoProduccion.Costo, prod.DisenoProduccion.Descripcion, DateTime.Now, 0, prod.DisenoProduccion.FechaService, true, 0, 0);
                        //Incremento autonumerado de idRobot cada vez que se crea uno nuevo
                        IdRobotAuto++;
                    }
                    componenteReal = Stock.Instancia.ObtenerComponente(prod.DisenoProduccion.Nombre);
                    //Inicializa ContadorCantidadSolicitada
                    ContadorCantidadSolicitada = 0;                    
                    //Aumento contador para llevar los diseños a producir
                    ContadorRobots++;
                    //Coloco la producción en false para no producir la misma nuevamente
                    prod.Producir = false;                    
                    //Produce cantidad solicitada
                    while (ContadorCantidadSolicitada < prod.CantidadSolicitada)
                    {
                        //Se produce siempre y cuando no supere su cantidad producida
                        if (prod.CantidadProducida < prod.CantidadSolicitada)
                        {
                            //Decremento la cantidad de los componentes en el stock
                            foreach (Componente comp in prod.DisenoProduccion.ListaComponentes)
                            {
                                Componente componente = Stock.Instancia.ObtenerComponente(comp.Modelo);                                                               
                                componente.Cantidad = componente.Cantidad - comp.Cantidad;
                           
                            }                            
                            //Subo la cantidad de su stock                             
                            componenteReal.Cantidad++;
                            //Aumento contador de cantidad solicitada del diseño
                            ContadorCantidadSolicitada++;
                            //Aumento cantidad producida de la producción
                            prod.CantidadProducida++;                                                      
                            Sistema.Instancia.ActualizarTodo(this);
                        }
                    }
                }
                
             }
            this.TimerProduccion.Stop();
            MessageBox.Show("Finalizo la Producción", "Información", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
        /// <summary>
        /// Crea timer para producción
        /// </summary>
        public void CrearTimerProduccion()
        {
            this.TimerProduccion = new System.Timers.Timer(this.SegundosProduccion * 1000);
            this.TimerProduccion.Enabled = true;            
            GC.KeepAlive(this.TimerProduccion);
        }
        /// <summary>
        /// Lista ordenada de producciones por su idproduccion
        /// </summary>
        /// <returns></returns>
        public List<Produccion> ListaComponentesOrdenada()
        {
            List<Produccion> list = (from Produccion p in ListaProducciones
                                     orderby p.DisenoProduccion.Medio ascending, p.FechaProduccionAgendada ascending
                                     select p).ToList();
            return list;
        }
        /// <summary>
        /// Verificar que la producción pueda ser relizada con el stock que existe
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        private bool VerificoProduccion(Produccion p)
        {
            foreach (Componente comp in p.DisenoProduccion.ListaComponentes)
            {
                int cantidadComp = comp.Cantidad * p.CantidadSolicitada;
                Componente componente = Stock.Instancia.ObtenerComponente(comp.Modelo);
                if (componente.Cantidad == 0 || (componente.Cantidad - cantidadComp) < 0)
                {
                    return false;
                }
            }
            return true;
        }

    }
}
